#ifndef	__attr_buffer_H__
#define	__attr_buffer_H__

#include "rendPlatform.hpp"
#include "rendRegister.hpp"
#include "rendVectorArray.hpp"
#include "rendAttrBufferBasis.hpp"
#include "rendVect.hpp"

#include <stdlib.h>
#include <memory.h>

#include <string>
#include <iostream>

namespace rend
{

////////////////////////////////////////////////////////////////////////////////////////////////
// mesh attribute buffer abstractor, user-space-mapping-disabled version
//
// provides:
// * container of mesh attribute data; keeps either vertex or index mesh attributes
// * data is passed to GL as AoS of customizable layout
////////////////////////////////////////////////////////////////////////////////////////////////

template < unsigned ATTR_CAPACITY_T = 16 >
class AttrBuffer
{
public:

	AttrBuffer();
	AttrBuffer(const AttrBuffer& src);				// copy ctor produces a blank GL slate

	~AttrBuffer();

	AttrBuffer& operator = (const AttrBuffer&);		// assignment op produces a blank GL slate

	////////////////////////////////////////////////////////////////////////////////////////////////
	// asset register support
	////////////////////////////////////////////////////////////////////////////////////////////////

	typedef naked::Register< std::string, AttrBuffer >	Register;

	static Register	s_default_register;

	////////////////////////////////////////////////////////////////////////////////////////////////
	// buffer type, length and content format
	////////////////////////////////////////////////////////////////////////////////////////////////

	enum BufferType
	{
		BUFFER_TYPE_NONE,
		BUFFER_TYPE_VERTEX,
		BUFFER_TYPE_INDEX
	};

	void					setBufferType(const BufferType type);
	BufferType				getBufferType() const;

	// mutator fails if buffer is locked
	bool					setBufferLength(const unsigned len);
	unsigned				getBufferLength() const;

	unsigned				getBufferRecSize() const;

	// mutator fails if buffer is locked
	bool					setAttrScript(const unsigned attr_count, const AttrDescriptor* first);

	unsigned				getAttrCount() const;
	const AttrDescriptor&	getAttrDescriptor(const unsigned attr_idx) const;

	unsigned				getAttrOffset(const unsigned attr_idx) const;

	// attribute index seeker, returns -1 for failure
	unsigned				findAttr(const AttrDescriptor& desc) const;

	////////////////////////////////////////////////////////////////////////////////////////////////
	// buffer actuator
	//
	// activates buffer; method fails if buffer is locked; name supplied for reporting purposes
	////////////////////////////////////////////////////////////////////////////////////////////////
	bool					actuate(const std::string& name) const;

	////////////////////////////////////////////////////////////////////////////////////////////////
	// buffer content (un)flattening 
	//
	// (un)flattens length * rec_size bytes of content; method fails if buffer is locked
	////////////////////////////////////////////////////////////////////////////////////////////////
	bool					contentFlatten(std::ostream&) const;
	bool					contentUnflatten(std::istream&);

	////////////////////////////////////////////////////////////////////////////////////////////////
	// content access
	////////////////////////////////////////////////////////////////////////////////////////////////

	bool acquireLock() const;

	void releaseLock() const;

	////////////////////////////////////////////////////////////////////////////////////////////////
	// raw bits accessor, mutable version
	//
	// returns a pointer to the first byte of the buffer;
	// requires a buffer lock - pointer is valid while lock lasts
	////////////////////////////////////////////////////////////////////////////////////////////////

	uint8* getRawBits() const;

	////////////////////////////////////////////////////////////////////////////////////////////////
	// raw bits accessor, immutable version
	//
	// returns a pointer to the first byte of the buffer;
	// requires a buffer lock - pointer is valid while lock lasts
	////////////////////////////////////////////////////////////////////////////////////////////////

	const uint8* getRawBitsImm() const;

	template < typename ELEMENT_TYPE_T, unsigned ELEMENT_COUNT_T >
	class AttrAccessorImm
	{
		friend class AttrBuffer< ATTR_CAPACITY_T >;

		const int8*		p;
		unsigned		n;
		unsigned		stride;

		AttrAccessorImm(const AttrAccessorImm&)
		{}

		AttrAccessorImm& operator =(const AttrAccessorImm&)
		{}

	public:

		AttrAccessorImm()
		: p(NULL)
		, n(0)
		, stride(0)
		{}

		unsigned count() const;

		const ELEMENT_TYPE_T (& get(const unsigned i) const)[ELEMENT_COUNT_T];
	};

	template < typename ELEMENT_TYPE_T, unsigned ELEMENT_COUNT_T >
	class AttrAccessor
	{
		friend class AttrBuffer< ATTR_CAPACITY_T >;

		int8*			p;
		unsigned		n;
		unsigned		stride;

		AttrAccessor(const AttrAccessor&)
		{}

		AttrAccessor& operator =(const AttrAccessor&)
		{}

	public:

		AttrAccessor()
		: p(NULL)
		, n(0)
		, stride(0)
		{}

		unsigned count() const;

		ELEMENT_TYPE_T (& get(const unsigned i) const)[ELEMENT_COUNT_T];

		operator const AttrAccessorImm< ELEMENT_TYPE_T, ELEMENT_COUNT_T >& () const;
	};

	////////////////////////////////////////////////////////////////////////////////////////////////
	// attribute accessor, mutable version
	//
	// fills out a mutable accessor to one of the attributes stored in this buffer;
	// requires a buffer lock
	////////////////////////////////////////////////////////////////////////////////////////////////

	template < typename ELEMENT_TYPE_T, unsigned ELEMENT_COUNT_T >
	bool getAttrAccessor(
		const AttrDescriptor& desc,										// attribute semantics
		AttrAccessor< ELEMENT_TYPE_T, ELEMENT_COUNT_T >& out);			// attribute accessor

	////////////////////////////////////////////////////////////////////////////////////////////////
	// attribute accessor, immutable version
	//
	// fills out an immutable accessor to one of the attributes stored in this buffer;
	// requires a buffer lock
	////////////////////////////////////////////////////////////////////////////////////////////////

	template < typename ELEMENT_TYPE_T, unsigned ELEMENT_COUNT_T >
	bool getAttrAccessor(
		const AttrDescriptor& desc,										// attribute semantics
		AttrAccessorImm< ELEMENT_TYPE_T, ELEMENT_COUNT_T >& out) const;	// attribute accessor

	////////////////////////////////////////////////////////////////////////////////////////////////
	// coherent bulk mutator, transfer-of-ownership, inbound version
	//
	// transfers the content of an array to this buffer, discarding any previous content;
	// agreement between array's and buffer's formats required;
	// array becomes empty at success;
	// mutator fails if buffer is locked
	////////////////////////////////////////////////////////////////////////////////////////////////

	template < typename ELEMENT_TYPE_T, unsigned ELEMENT_COUNT_T >
	bool setBulkTransfer(
		VectorArray< ELEMENT_TYPE_T, ELEMENT_COUNT_T >& src);			// attribute array

	////////////////////////////////////////////////////////////////////////////////////////////////
	// coherent bulk mutator, transfer-of-ownership, outbound version 
	//
	// transfers the content of this buffer to an array, discarding any previous content;
	// agreement between array's and buffer's formats required;
	// buffer becomes empty at success;
	// mutator fails if buffer is locked
	////////////////////////////////////////////////////////////////////////////////////////////////

	template < typename ELEMENT_TYPE_T, unsigned ELEMENT_COUNT_T >
	bool getBulkTransfer(
		VectorArray< ELEMENT_TYPE_T, ELEMENT_COUNT_T >& dst);			// attribute array

	////////////////////////////////////////////////////////////////////////////////////////////////
	// coherent bulk mutator, content-copy
	//
	// copies the content of an array to this buffer, discarding any previous content;
	// agreement between array's and buffer's formats required;
	// mutator fails if buffer is locked
	////////////////////////////////////////////////////////////////////////////////////////////////

	template < typename ELEMENT_TYPE_T, unsigned ELEMENT_COUNT_T >
	bool setBulkCopy(
		const VectorArrayImm< ELEMENT_TYPE_T, ELEMENT_COUNT_T >& src);	// attribute array

	////////////////////////////////////////////////////////////////////////////////////////////////
	// coherent bulk accessor, immutable version
	//
	// reflects the content of this buffer to an immutable array, discarding any previous content;
	// agreement between array's and buffer's formats required;
	// requires a buffer lock - reflection maintained while lock lasts
	////////////////////////////////////////////////////////////////////////////////////////////////

	template < typename ELEMENT_TYPE_T, unsigned ELEMENT_COUNT_T >
	bool getBulkReflect(
		VectorArrayImm< ELEMENT_TYPE_T, ELEMENT_COUNT_T >& dst) const;	// attribute array

private:

	BufferType				m_type;								// buffer type
	unsigned				m_rec_size;							// attribute record size as per script
	unsigned				m_len;								// buffer length, in records
	mutable void*			m_bits;								// buffer data

	unsigned				m_attr_count;						// attribute count
	AttrDescriptor			m_attr_desc[ATTR_CAPACITY_T];		// attribute script
	unsigned				m_attr_offset[ATTR_CAPACITY_T];		// attribute offset

	mutable bool			m_lock;								// buffer locked
	mutable bool			m_dirty;							// buffer content upated
	mutable GLuint			m_vbo;								// GL name
};


typedef AttrBuffer< 16 >	AttrBuffer16;						// attribute buffer of capacity 16 attributes


template < unsigned ATTR_CAPACITY_T >
inline AttrBuffer< ATTR_CAPACITY_T >::AttrBuffer()
: m_type(BUFFER_TYPE_NONE)
, m_rec_size(0)
, m_len(0)
, m_bits(NULL)
, m_attr_count(0)
, m_lock(false)
, m_dirty(false)
, m_vbo(0)
{
}


template < unsigned ATTR_CAPACITY_T >
inline AttrBuffer< ATTR_CAPACITY_T >::AttrBuffer(const AttrBuffer< ATTR_CAPACITY_T >& src)
: m_type(src.m_type)
, m_rec_size(src.m_rec_size)
, m_len(src.m_len)
, m_bits(NULL)
, m_attr_count(src.m_attr_count)
, m_lock(false)
, m_dirty(false)
, m_vbo(0)
{
	for (unsigned i = 0; i < m_attr_count; ++i)
	{
		m_attr_desc[i] = src.m_attr_desc[i];
		m_attr_offset[i] = src.m_attr_offset[i];
	}

	if (src.m_bits)
	{
		assert(m_len && m_rec_size);

		const unsigned size = m_len * m_rec_size;

		m_bits = malloc(size);
		assert(m_bits);

		memcpy(m_bits, src.m_bits, size);

		m_dirty = true;
	}
}


template < unsigned ATTR_CAPACITY_T >
inline AttrBuffer< ATTR_CAPACITY_T >::~AttrBuffer()
{
	free(m_bits);

	if (m_vbo)
	{
		printf("discarding vbo id 0x%08x\n", m_vbo);

		glDeleteBuffersARB(1, &m_vbo);

		if (reportGLError(stderr))
			fprintf(stderr, "failure deleting vbo\n");
	}
}


template < unsigned ATTR_CAPACITY_T >
inline AttrBuffer< ATTR_CAPACITY_T >& AttrBuffer<ATTR_CAPACITY_T>::operator = (const AttrBuffer< ATTR_CAPACITY_T >& src)
{
	this->~AttrBuffer();
	
	return *new (this) AttrBuffer< ATTR_CAPACITY_T >(src);
}


template < unsigned ATTR_CAPACITY_T >
inline void AttrBuffer< ATTR_CAPACITY_T >::setBufferType(const BufferType type)
{
	assert(BUFFER_TYPE_NONE == m_type || m_type == type);

	m_type = type;
}


template < unsigned ATTR_CAPACITY_T >
inline typename AttrBuffer< ATTR_CAPACITY_T >::BufferType AttrBuffer< ATTR_CAPACITY_T >::getBufferType() const
{
	return m_type;
}


template < unsigned ATTR_CAPACITY_T >
inline bool AttrBuffer< ATTR_CAPACITY_T >::setBufferLength(const unsigned len)
{
	if (m_lock)
		return false;

	if (m_len == len)
		return true;

	m_len = len;

	free(m_bits);
	m_bits = NULL;

	m_dirty = false;

	return true;
}


template < unsigned ATTR_CAPACITY_T >
inline unsigned AttrBuffer< ATTR_CAPACITY_T >::getBufferLength() const
{
	return m_len;
}


template < unsigned ATTR_CAPACITY_T >
inline unsigned	AttrBuffer< ATTR_CAPACITY_T >::getBufferRecSize() const
{
	return m_rec_size;
}


template < unsigned ATTR_CAPACITY_T >
inline bool AttrBuffer< ATTR_CAPACITY_T >::setAttrScript(
	const unsigned attr_count,
	const AttrDescriptor* first_attr)
{
	if (m_type == BUFFER_TYPE_NONE)
		return false;

	assert(m_type != BUFFER_TYPE_INDEX || attr_count == 1);

	if (m_lock)
		return false;

	if (attr_count > ATTR_CAPACITY_T)
		return false;

	if (attr_count && first_attr == NULL)
		return false;

	m_attr_count = attr_count;
	m_rec_size = 0;

	free(m_bits);
	m_bits = NULL;

	for (unsigned i = 0; i < m_attr_count; ++i)
	{
		m_attr_desc[i] = first_attr[i];
		m_attr_offset[i] = m_rec_size;

		m_rec_size += m_attr_desc[i].element_count * sizeofAttrElement(m_attr_desc[i].element_type);
	}

	m_dirty = false;

	return m_rec_size != 0;
}


template < unsigned ATTR_CAPACITY_T >
inline unsigned AttrBuffer< ATTR_CAPACITY_T >::getAttrCount() const
{
	return m_attr_count;
}


template < unsigned ATTR_CAPACITY_T >
inline const AttrDescriptor& AttrBuffer< ATTR_CAPACITY_T >::getAttrDescriptor(
	const unsigned attr_idx) const
{
	assert(attr_idx < m_attr_count);

	return m_attr_desc[attr_idx];
}


template < unsigned ATTR_CAPACITY_T >
inline unsigned	AttrBuffer< ATTR_CAPACITY_T >::getAttrOffset(
	const unsigned attr_idx) const
{
	assert(attr_idx < m_attr_count);

	return m_attr_offset[attr_idx];
}


template < unsigned ATTR_CAPACITY_T >
inline unsigned AttrBuffer< ATTR_CAPACITY_T >::findAttr(
	const AttrDescriptor& desc) const
{
	unsigned i = 0;

	for (; i < m_attr_count && m_attr_desc[i] != desc; ++i);

	if (i == m_attr_count)
		return -1U;

	return i;
}


template < unsigned ATTR_CAPACITY_T >
inline bool AttrBuffer< ATTR_CAPACITY_T >::acquireLock() const
{
	if (m_type == BUFFER_TYPE_NONE)
		return false;

	if (!m_len || !m_rec_size)
		return false;

	if (!m_bits)
	{
		m_bits = malloc(m_len * m_rec_size);
		assert(m_bits);

		if (!m_bits)
			return false;
	}

	m_lock = true;

	return true;
}


template < unsigned ATTR_CAPACITY_T >
inline void AttrBuffer< ATTR_CAPACITY_T >::releaseLock() const
{
	m_lock = false;
}


template < unsigned ATTR_CAPACITY_T >
inline uint8* AttrBuffer< ATTR_CAPACITY_T >::getRawBits() const
{
	if (m_lock)
	{
		m_dirty = true;

		return *reinterpret_cast< uint8** >(&m_bits);
	}

	return 0;
}


template < unsigned ATTR_CAPACITY_T >
inline const uint8* AttrBuffer< ATTR_CAPACITY_T >::getRawBitsImm() const
{
	if (m_lock)
		return *reinterpret_cast< const uint8** >(&m_bits);

	return 0;
}


template < unsigned ATTR_CAPACITY_T >
template < typename ELEMENT_TYPE_T, unsigned ELEMENT_COUNT_T >
unsigned AttrBuffer< ATTR_CAPACITY_T >::AttrAccessorImm< ELEMENT_TYPE_T, ELEMENT_COUNT_T >::count() const
{
	return n;
}


template < unsigned ATTR_CAPACITY_T >
template < typename ELEMENT_TYPE_T, unsigned ELEMENT_COUNT_T >
inline const ELEMENT_TYPE_T (& AttrBuffer< ATTR_CAPACITY_T >::AttrAccessorImm< ELEMENT_TYPE_T, ELEMENT_COUNT_T >::get(const unsigned i) const)[ELEMENT_COUNT_T]
{
	assert(p && stride && i < n);

	return *reinterpret_cast< const ELEMENT_TYPE_T (*)[ELEMENT_COUNT_T] >(p + i * stride);
}


template < unsigned ATTR_CAPACITY_T >
template < typename ELEMENT_TYPE_T, unsigned ELEMENT_COUNT_T >
inline unsigned AttrBuffer< ATTR_CAPACITY_T >::AttrAccessor< ELEMENT_TYPE_T, ELEMENT_COUNT_T >::count() const
{
	return n;
}


template < unsigned ATTR_CAPACITY_T >
template < typename ELEMENT_TYPE_T, unsigned ELEMENT_COUNT_T >
inline ELEMENT_TYPE_T (& AttrBuffer< ATTR_CAPACITY_T >::AttrAccessor< ELEMENT_TYPE_T, ELEMENT_COUNT_T >::get(const unsigned i) const)[ELEMENT_COUNT_T]
{
	assert(p && stride && i < n);

	return *reinterpret_cast< ELEMENT_TYPE_T (*)[ELEMENT_COUNT_T] >(p + i * stride);
}


template < unsigned ATTR_CAPACITY_T >
template < typename ELEMENT_TYPE_T, unsigned ELEMENT_COUNT_T >
inline AttrBuffer< ATTR_CAPACITY_T >::AttrAccessor< ELEMENT_TYPE_T, ELEMENT_COUNT_T >::operator
	const typename AttrBuffer< ATTR_CAPACITY_T >::template AttrAccessorImm< ELEMENT_TYPE_T, ELEMENT_COUNT_T >& () const
{
	return *reinterpret_cast< const AttrAccessorImm< ELEMENT_TYPE_T, ELEMENT_COUNT_T >* >(this);
}


template < unsigned ATTR_CAPACITY_T >
template < typename ELEMENT_TYPE_T, unsigned ELEMENT_COUNT_T >
inline bool AttrBuffer< ATTR_CAPACITY_T >::getAttrAccessor(
	const AttrDescriptor& desc,
	AttrAccessor< ELEMENT_TYPE_T, ELEMENT_COUNT_T >& out)
{
	assert(sizeof(ELEMENT_TYPE_T) == sizeofAttrElement(desc.element_type));
	assert(ELEMENT_COUNT_T == desc.element_count);

	if (!m_lock)
		return false;

	assert(m_bits != NULL);
	assert(m_len && m_rec_size);

	unsigned idx;

	if (-1U == (idx = findAttr(desc)))
		return false;

	out.p = reinterpret_cast< int8* >(m_bits) + m_attr_offset[idx];
	out.n = m_len;
	out.stride = m_rec_size;

	m_dirty = true;

	return true;
}


template < unsigned ATTR_CAPACITY_T >
template < typename ELEMENT_TYPE_T, unsigned ELEMENT_COUNT_T >
inline bool AttrBuffer< ATTR_CAPACITY_T >::getAttrAccessor(
	const AttrDescriptor& desc,
	AttrAccessorImm< ELEMENT_TYPE_T, ELEMENT_COUNT_T >& out) const
{
	assert(sizeof(ELEMENT_TYPE_T) == sizeofAttrElement(desc.element_type));
	assert(ELEMENT_COUNT_T == desc.element_count);

	if (!m_lock)
		return false;

	assert(m_bits != NULL);
	assert(m_len && m_rec_size);

	unsigned idx;

	if (-1U == (idx = findAttr(desc)))
		return false;

	out.p = reinterpret_cast< const int8* >(m_bits) + m_attr_offset[idx];
	out.n = m_len;
	out.stride = m_rec_size;

	return true;
}


template < unsigned ATTR_CAPACITY_T >
template < typename ELEMENT_TYPE_T, unsigned ELEMENT_COUNT_T >
inline bool AttrBuffer< ATTR_CAPACITY_T >::setBulkTransfer(
	VectorArray< ELEMENT_TYPE_T, ELEMENT_COUNT_T >& src)
{
	if (m_rec_size != sizeof(ELEMENT_TYPE_T[ELEMENT_COUNT_T]))
		return false;

	if (m_lock)
		return false;

	unsigned total_element_count = 0;

	for (unsigned i = 0; i < m_attr_count; ++i)
	{
		if (sizeofAttrElement(m_attr_desc[i].element_type) != sizeof(ELEMENT_TYPE_T))
			return false;

		total_element_count += m_attr_desc[i].element_count;
	}

	if (total_element_count != ELEMENT_COUNT_T)
		return false;

	free(m_bits);

	src.drain(*reinterpret_cast< ELEMENT_TYPE_T (**)[ELEMENT_COUNT_T] >(&m_bits), m_len);

	m_dirty = true;

	return true;
}


template < unsigned ATTR_CAPACITY_T >
template < typename ELEMENT_TYPE_T, unsigned ELEMENT_COUNT_T >
inline bool AttrBuffer< ATTR_CAPACITY_T >::getBulkTransfer(
	VectorArray< ELEMENT_TYPE_T, ELEMENT_COUNT_T >& dst)
{
	if (m_rec_size != sizeof(ELEMENT_TYPE_T[ELEMENT_COUNT_T]))
		return false;

	if (m_lock)
		return false;

	unsigned total_element_count = 0;

	for (unsigned i = 0; i < m_attr_count; ++i)
	{
		if (sizeofAttrElement(m_attr_desc[i].element_type) != sizeof(ELEMENT_TYPE_T))
			return false;

		total_element_count += m_attr_desc[i].element_count;
	}

	if (total_element_count != ELEMENT_COUNT_T)
		return false;

	dst.absorb(reinterpret_cast< ELEMENT_TYPE_T (*)[ELEMENT_COUNT_T] >(m_bits), m_len);

	m_bits = NULL;
	m_len = 0;

	m_dirty = false;

	return true;
}


template < unsigned ATTR_CAPACITY_T >
template < typename ELEMENT_TYPE_T, unsigned ELEMENT_COUNT_T >
inline bool AttrBuffer< ATTR_CAPACITY_T >::setBulkCopy(
	const VectorArrayImm< ELEMENT_TYPE_T, ELEMENT_COUNT_T >& src)
{
	if (m_rec_size != sizeof(ELEMENT_TYPE_T[ELEMENT_COUNT_T]))
		return false;

	if (m_lock)
		return false;

	unsigned total_element_count = 0;

	for (unsigned i = 0; i < m_attr_count; ++i)
	{
		if (sizeofAttrElement(m_attr_desc[i].element_type) != sizeof(ELEMENT_TYPE_T))
			return false;

		total_element_count += m_attr_desc[i].element_count;
	}

	if (total_element_count != ELEMENT_COUNT_T)
		return false;

	m_bits = realloc(m_bits, src.count() * sizeof(ELEMENT_TYPE_T[ELEMENT_COUNT_T]));
	m_len = src.count();

	for (unsigned i = 0; i < src.count(); ++i)
		vect_generic< ELEMENT_TYPE_T, ELEMENT_COUNT_T >::cast(reinterpret_cast< ELEMENT_TYPE_T (*)[ELEMENT_COUNT_T] >(m_bits)[i]) =
			vect_generic< ELEMENT_TYPE_T, ELEMENT_COUNT_T >::cast(src.get(i));

	m_dirty = true;

	return true;
}


template < unsigned ATTR_CAPACITY_T >
template < typename ELEMENT_TYPE_T, unsigned ELEMENT_COUNT_T >
inline bool AttrBuffer< ATTR_CAPACITY_T >::getBulkReflect(
	VectorArrayImm< ELEMENT_TYPE_T, ELEMENT_COUNT_T >& dst) const
{
	if (m_rec_size != sizeof(ELEMENT_TYPE_T[ELEMENT_COUNT_T]))
		return false;

	if (!m_lock)
		return false;

	unsigned total_element_count = 0;

	for (unsigned i = 0; i < m_attr_count; ++i)
	{
		if (sizeofAttrElement(m_attr_desc[i].element_type) != sizeof(ELEMENT_TYPE_T))
			return false;

		total_element_count += m_attr_desc[i].element_count;
	}

	if (total_element_count != ELEMENT_COUNT_T)
		return false;

	dst = VectorArrayImm< ELEMENT_TYPE_T, ELEMENT_COUNT_T >(
		reinterpret_cast< ELEMENT_TYPE_T (*)[ELEMENT_COUNT_T] >(m_bits), m_len);

	return true;
}


template < unsigned ATTR_CAPACITY_T >
inline bool AttrBuffer< ATTR_CAPACITY_T >::actuate(
	const std::string& name) const
{
	if (BUFFER_TYPE_NONE == m_type)
		return false;

	if (m_lock)
		return false;

	if (!m_bits)
		return false;

	assert(m_len && m_rec_size);

	if (!m_vbo)
	{
		glGenBuffersARB(1, &m_vbo);

		if (!m_vbo)
		{
			fprintf(stderr, "error: failed to obtain vbo name for '%s'\n", name.c_str());
			return false;
		}

		printf("vbo id 0x%08x '%s'\n", m_vbo, name.c_str());
	}

	switch (m_type)
	{
		case BUFFER_TYPE_VERTEX:

			glBindBufferARB(GL_ARRAY_BUFFER_ARB, m_vbo);

			if (!m_dirty)
				break;

			glBufferDataARB(GL_ARRAY_BUFFER_ARB, m_len * m_rec_size, m_bits, GL_STATIC_DRAW_ARB);
			break;

		case BUFFER_TYPE_INDEX:

			glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, m_vbo);

			if (!m_dirty)
				break;

			glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, m_len * m_rec_size, m_bits, GL_STATIC_DRAW_ARB);
			break;
	}

	const bool success = !reportGLError(stderr);

	if (success)
		m_dirty = false;

	return success;
}


template < unsigned ATTR_CAPACITY_T >
bool AttrBuffer< ATTR_CAPACITY_T >::contentFlatten(std::ostream& dst) const
{
	if (m_lock || !acquireLock())
		return false;

	dst.write(reinterpret_cast< const char* >(m_bits), std::streamsize(m_len * m_rec_size));

	releaseLock();

	return dst.good();
}


template < unsigned ATTR_CAPACITY_T >
bool AttrBuffer< ATTR_CAPACITY_T >::contentUnflatten(std::istream& src)
{
	if (m_lock || !acquireLock())
		return false;

	src.read(reinterpret_cast< char* >(m_bits), std::streamsize(m_len * m_rec_size));

	releaseLock();
	m_dirty = true;

	return src.good();
}

} // namespace rend

#endif // __attr_buffer_H__
